// 服务端

#include <iostream>
using namespace std;

#include <WinSock2.h> //windows 下使用套接字需要包含
#include <WS2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

int main(void)
{
	cout << "服务端程序开始运行" << endl;

	//在Windows中使用套接字需要先加载套接字库（套接字环境），最后需要释放套接字资源。
	WSADATA wsaData;
	int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (result != 0)
	{
		cout << "WSAStartup failed: " << result << endl;
		return 1;
	}

	// 1、创建监听的套接字
	// AF_INET 使用IPV4
	// SOCK_STREAM 使用流式协议，即TCP  SOCK_DGRAM 使用报文协议，即UDP
	// IPPROTO_TCP, 使用指定的流式协议中的tcp协议 IPPROTO_UDP, 使用指定的报式协议中的udp协议
	SOCKET fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (fd == -1)
	{
		cout << "套接字创建失败" << fd << endl;
		return -1;
	}

	// 2、绑定本地IP 端口	
	sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(9999);//找一个没有被使用的端口，一般5000以上操作系统就不占用了
	saddr.sin_addr.s_addr = INADDR_ANY; //0 0.0.0.0 指定为0，自动去读本地网卡指定的IP地址
	int result_bind = bind(fd, (sockaddr*)&saddr, sizeof(saddr));
	if (result_bind == -1)
	{
		cout << "绑定失败 " << result_bind << endl;
		return -1;
	}

	// 3、设置监听
	int result_listen = listen(fd, 128);
	if (result_listen == -1)
	{
		cout << "设置监听失败 " << result_listen << endl;
		return -1;
	}

	// 4、阻塞并等待客户端连接
	sockaddr_in caddr;
	int addrlen = sizeof(caddr);
	SOCKET cfd = accept(fd, (sockaddr*)&caddr, &addrlen);
	if (cfd == -1)
	{
		cout << "失败 " << cfd << endl;
		return -1;
	}
	// 连接成功，打印客户端的IP和端口信息
	char ip[32];
	cout << "客户端的IP：  " << inet_ntop(AF_INET, &caddr.sin_addr.s_addr, ip, sizeof(ip)) << endl;
	cout << "客户端的端口： " << ntohs(caddr.sin_port) << endl;

	// 5、通信
	char receiveBuffer[1024];
	int receiveLength = 0;
	char sendBuffer[1024];
	int sendLength = 0;
	while (1)
	{
		receiveLength = recv(cfd, receiveBuffer, sizeof(receiveBuffer), 0);//阻塞的等
		if (receiveLength > 0)
		{
			cout << "接收到数据：" << endl;
			cout << receiveBuffer << endl;

			// 将数据发回去，表明接收
			sendLength = sprintf_s(sendBuffer, "服务端接收到数据： %s", receiveBuffer);
			send(cfd, sendBuffer, sendLength, 0);
		}
		else if (receiveLength == 0)
		{
			cout << "客户端断开了连接" << endl;
			break;
		}
		else
		{
			cout << "出现错误" << endl;
			break;
		}
	}

	// 关闭描述符
	
	closesocket(fd);
	closesocket(cfd);

	WSACleanup();//windows下需要清理

	cout << "服务端程序停止运行" << endl;

	system("pause");

	return 0;
}